package com.jh.user.pojo.freeQuick;

import cn.jh.common.core.json.JsonKt;
import cn.jh.common.pay.jdpush.CalFeeStatus;
import cn.jh.common.pay.jdpush.FreeQuickRequestType;
import cn.jh.common.pay.jdpush.OrderStatus;
import cn.jh.common.pay.utils.PayBarConst;
import cn.jh.common.pay.utils.PayBarResponse;
import com.google.common.base.MoreObjects;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;

import static org.springframework.util.StringUtils.hasText;

/**
 * @author Yichuan
 * 订单信息表
 * @version 1.0
 */
@Entity
@Table(name = "t_geway_bank_card")
@EntityListeners(AuditingEntityListener.class)
public class PayOrder extends AbstractRecording<Long> {

    private static final long serialVersionUID = -2271002842347105982L;

    @Column(name = "order_no")
    private String orderNo;

    @Column(name = "out_serial_no")
    private String outSerialNo;

    @Column(name = "merchant_id")
    private String merchantId;

    @Column(name = "merchant_no")
    private String merchantNo;

    @Enumerated(EnumType.STRING)
    @Column(name = "channel")
    private ChannelCode channel;

    @Column(name = "bank_card_no")
    private String bankCardNo;

    @Column(name = "cvn2")
    private String cvn2; //(信用卡必填)

    @Column(name = "expired")
    private String expiredDate; //(信用卡必填)

    @Column(name = "amount")
    private BigDecimal amount;  //BigDecimal(10,2)

    @Column(name = "receive_fee")
    private BigDecimal receiveFee;

    @Column(name = "receive_amount")
    private BigDecimal receiveAmount;

    @Column(name = "poundage")
    private BigDecimal poundage;

    @Column(name = "channel_fee")
    private BigDecimal channelFee;

    @Column(name = "actual_amount")
    private BigDecimal actualAmount;

    @Enumerated(EnumType.ORDINAL)
    @Column(name = "fee_status")
    private CalFeeStatus feeStatus;

    @Column(name = "productName")
    private String productName;

    @Column(name = "product_desc")
    private String productDesc;

    @Column(name = "notify_url")
    private String notifyUrl;

    @Enumerated(EnumType.STRING)
    @Column(name = "order_status")
    private OrderStatus orderStatus;

    @Column(name = "remark")
    private String remark;

    @Column(name = "notify_content")
    private String notifyContent;

    public PayOrder() {
        super();
    }

    public String getOrderNo() {
        return orderNo;
    }

    public void setOrderNo(String orderNo) {
        this.orderNo = orderNo;
    }

    public String getOutSerialNo() {
        return outSerialNo;
    }

    public void setOutSerialNo(String outSerialNo) {
        this.outSerialNo = outSerialNo;
    }

    public String getMerchantId() {
        return merchantId;
    }

    public void setMerchantId(String merchantId) {
        this.merchantId = merchantId;
    }

    public String getMerchantNo() {
        return merchantNo;
    }

    public void setMerchantNo(String merchantNo) {
        this.merchantNo = merchantNo;
    }

    public ChannelCode getChannel() {
        return channel;
    }

    public void setChannel(ChannelCode channel) {
        this.channel = channel;
    }

    public String getBankCardNo() {
        return bankCardNo;
    }

    public void setBankCardNo(String bankCardNo) {
        this.bankCardNo = bankCardNo;
    }

    public String getCvn2() {
        return cvn2;
    }

    public void setCvn2(String cvn2) {
        this.cvn2 = cvn2;
    }

    public String getExpiredDate() {
        return expiredDate;
    }

    public void setExpiredDate(String expiredDate) {
        this.expiredDate = expiredDate;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public BigDecimal getReceiveFee() {
        return receiveFee;
    }

    public void setReceiveFee(BigDecimal receiveFee) {
        this.receiveFee = receiveFee;
    }

    public BigDecimal getPoundage() {
        return poundage;
    }

    public void setPoundage(BigDecimal poundage) {
        this.poundage = poundage;
    }

    public BigDecimal getChannelFee() {
        return channelFee;
    }

    public void setChannelFee(BigDecimal channelFee) {
        this.channelFee = channelFee;
    }

    public BigDecimal getActualAmount() {
        return actualAmount;
    }

    public void setActualAmount(BigDecimal actualAmount) {
        this.actualAmount = actualAmount;
    }

    public BigDecimal getReceiveAmount() {
        return receiveAmount;
    }

    public void setReceiveAmount(BigDecimal receiveAmount) {
        this.receiveAmount = receiveAmount;
    }

    public CalFeeStatus getFeeStatus() {
        return feeStatus;
    }

    public void setFeeStatus(CalFeeStatus feeStatus) {
        this.feeStatus = feeStatus;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductDesc() {
        return productDesc;
    }

    public void setProductDesc(String productDesc) {
        this.productDesc = productDesc;
    }

    public String getNotifyUrl() {
        return notifyUrl;
    }

    public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
    }

    public OrderStatus getOrderStatus() {
        return orderStatus;
    }

    public void setOrderStatus(OrderStatus orderStatus) {
        this.orderStatus = orderStatus;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public String getNotifyContent() {
        return notifyContent;
    }

    public void setNotifyContent(String notifyContent) {
        this.notifyContent = notifyContent;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PayOrder)) return false;
        if (!super.equals(o)) return false;
        PayOrder that = (PayOrder) o;
        return Objects.equals(getOrderNo(), that.getOrderNo()) &&
                Objects.equals(getOutSerialNo(), that.getOutSerialNo()) &&
                Objects.equals(getMerchantId(), that.getMerchantId()) &&
                Objects.equals(getMerchantNo(), that.getMerchantNo()) &&
                Objects.equals(getChannel(), that.getChannel()) &&
                Objects.equals(getBankCardNo(), that.getBankCardNo()) &&
                Objects.equals(getCvn2(), that.getCvn2()) &&
                Objects.equals(getExpiredDate(), that.getExpiredDate()) &&
                Objects.equals(getAmount(), that.getAmount()) &&
                Objects.equals(getPoundage(), that.getPoundage()) &&
                Objects.equals(getChannelFee(), that.getChannelFee()) &&
                Objects.equals(getActualAmount(), that.getActualAmount()) &&
                Objects.equals(getReceiveFee(), that.getReceiveFee()) &&
                Objects.equals(getReceiveAmount(), that.getReceiveAmount()) &&
                getFeeStatus() == that.getFeeStatus() &&
                Objects.equals(getProductName(), that.getProductName()) &&
                Objects.equals(getProductDesc(), that.getProductDesc()) &&
                Objects.equals(getNotifyUrl(), that.getNotifyUrl()) &&
                getOrderStatus() == that.getOrderStatus() &&
                Objects.equals(getRemark(), that.getRemark()) &&
                Objects.equals(getNotifyContent(), that.getNotifyContent()) &&
                Objects.equals(getUpdatedAt(), that.getUpdatedAt()) &&
                Objects.equals(getCreatedAt(), that.getCreatedAt());
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), getOrderNo(), getOutSerialNo(),
                getMerchantId(), getMerchantNo(), getChannel(), getBankCardNo(),
                getCvn2(), getExpiredDate(), getAmount(), getPoundage(), getChannelFee(),
                getActualAmount(), getReceiveFee(), getReceiveAmount(), getFeeStatus(),
                getProductName(), getProductDesc(), getNotifyUrl(), getOrderStatus(),
                getRemark(), getNotifyContent(), getUpdatedAt(), getCreatedAt());
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("orderNo", orderNo)
                .add("outSerialNo", outSerialNo)
                .add("merchantId", merchantId)
                .add("merchantNo", merchantNo)
                .add("channel", channel)
                .add("bankCardNo", bankCardNo)
                .add("cvn2", cvn2)
                .add("expiredDate", expiredDate)
                .add("amount", amount)
                .add("poundage", poundage)
                .add("channelFee", channelFee)
                .add("actualAmount", actualAmount)
                .add("receiveFee", receiveFee)
                .add("receiveAmount", receiveAmount)
                .add("feeStatus", feeStatus)
                .add("productName", productName)
                .add("productDesc", productDesc)
                .add("notifyUrl", notifyUrl)
                .add("orderStatus", orderStatus)
                .add("remark", remark)
                .add("notifyContent", notifyContent)
                .add("updatedAt", getUpdatedAt())
                .add("createdAt", getCreatedAt())
                .toString();
    }

    public static PayOrder newOrder(InConsumption ic, MerchantRateDTO rateDTO, ChannelCode channel) {
        PayOrder payOrder = new PayOrder();
        payOrder.setOrderNo(PayBarConst.getOrderIdByUUId(FreeQuickRequestType.PAY.getCode()));
        payOrder.setOutSerialNo(ic.getOutSerialNo());
        payOrder.setMerchantId(ic.getAppId());
        payOrder.setMerchantNo(rateDTO.getMerchantNo());
        payOrder.setChannel(channel);
        payOrder.setBankCardNo(ic.getCardNo());
        payOrder.setCvn2(ic.getCardCvn2());
        payOrder.setExpiredDate(ic.getCardExpired());
        BigDecimal amount = ic.getAmount();
        payOrder.setAmount(amount);
        payOrder.setProductName(ic.getProductName());
        payOrder.setProductDesc(ic.getProductDesc());
        payOrder.setNotifyUrl(ic.getNotifyUrl());
        payOrder.setOrderStatus(OrderStatus.UN_PAY);
        return payOrder;
    }

    public boolean isSuccess() {
        return getOrderStatus() == OrderStatus.SUCCESS;
    }

    public boolean isFailure() {
        return getOrderStatus() == OrderStatus.FAILURE;
    }

    public PayOrder payNotify(PayNotify notify, MerchantRateDTO rate) {
        setOrderStatus(notify.getStatus());
        if (notify.getFee() != null) {
            // 上游的扣费
            setReceiveFee(notify.getFee());
            // 接收上游的金额
            setReceiveAmount(getAmount().subtract(notify.getFee()));
            // 手续费
            setPoundage(rate.getExtraFee());
            // 通道费(订单金额 * (商户费率 - 上游给平台的费率))
            BigDecimal channelFee = getAmount().multiply(rate.getCreditRate().subtract(BigDecimal.valueOf(0.0045)));
            setChannelFee(channelFee);
            // 实际金额(订单金额-上游扣费-平台通道费-平台手续费)
            setActualAmount(getAmount().subtract(getReceiveFee()).subtract(channelFee).subtract(getPoundage()));
        }
        setFeeStatus(notify.getFeeStatus());
        setNotifyContent(notify.getContent());
        return this;
    }

    public PayOrder notify(PayBarResponse response, MerchantRateDTO rate) {
        setOrderStatus(OrderStatus.of(response.getField("status")));
        String fee = response.getField("fee");
        if (hasText(fee)) {
            setReceiveFee(new BigDecimal(fee).setScale(2, RoundingMode.HALF_UP));
            setReceiveAmount(getAmount().subtract(getReceiveFee()));
            setPoundage(rate.getExtraFee());
            // 通道费(订单金额 * 费率)
            BigDecimal channelFee = getAmount().multiply(rate.getCreditRate());
            setChannelFee(channelFee);
            setActualAmount(getAmount().subtract(rate.getExtraFee()).subtract(channelFee));
        }
        String isCalFee = response.getField("isCalFee");
        setFeeStatus(hasText(isCalFee) ? (Objects.equals(isCalFee, "1") ? CalFeeStatus.YES : CalFeeStatus.NO): CalFeeStatus.NO);
        setNotifyContent(JsonKt.toJSONString(response));
        return this;
    }
}